<?php
/***********************************************************************
 * @package     IvanEngine
 * @subpackage  Framwork
 * @author      Ivan <ivanzx@msn.com>
 * @created     2010-05-30
 ***********************************************************************/
if(defined("IVANENGINE_FRAMEWORK_CACHEMANAGER"))
    die("Redefinition macro in :".__FILE__."(".__LINE__.")");
define("IVANENGINE_FRAMEWORK_CACHEMANAGER", true);

require_once (dirname(__FILE__).'/Common.php');

class CCacheCallback extends CCallback
{
    public function __construct($method, $object = null, $param = array()) 
    {
        parent::__construct($method, $object);
        $this->SetParam($param);
    }
    
    public function __destruct() 
    {
        parent::__destruct();
    }
    
    public function Call()
    {
        if( $this->m_param < 4 ){
            throw new CExceptionInvalidArgument('Parameter Invalid!');
        } 
        $arguments = $this->m_param;
        if( $this->m_object ){
            return call_user_func(array($this->m_object, $this->m_method), 
                $arguments[0], $arguments[1], $arguments[2], $arguments[3]);
        }else{
            return call_user_func($this->m_method, 
                $arguments[0], $arguments[1], $arguments[2], $arguments[3]);
        }
    }
    
    public function __clone()
    {
        return new CCallback($this->m_method, $this->m_object);
    }
}

class CCacheManager extends CSingleton
{
    protected
        $m_cachePath    = null,
        $m_listDataPath = null,
        $m_isNeedSave   = false,
        $m_cacheFiles = null;
        
    static protected $m_this= null;
    
    public function __construct()
    {
        parent::__construct();
    }
    
    public function __destruct()
    {
        if( $this->m_isNeedSave ){
            $data = serialize($this->m_cacheFiles);
            CFileHelper::Write($this->m_listDataPath, $data);
        }
        unset($this->m_cacheFiles);
        parent::__destruct();
    }
    
    static public function Get()
    {
        if( self::$m_this == null )
        {
            parent::StartSingleton();
            self::$m_this = new CCacheManager();
            parent::EndSingleton();
        }
        return self::$m_this;
    }
    
    public function Initialize($cachePath)
    {
        $this->m_cachePath  = CFileHelper::CorrectPath($cachePath);
        if( $this->m_cachePath == null ){
            throw new CExceptionInvalidArgument('<b>CCacheManager initialize failed:<b><br>'.
                'You must use CConfig::Option(CConfig::OT_CachePath, "") to set the cache path.');
        }
        $this->m_listDataPath   = $this->GetCacheFilePath('Cache.dat');
        if( !CFileHelper::IsExsit($this->m_listDataPath) ){
            $this->m_cacheFiles   = new CMap();
        }else{
            $data = CFileHelper::Read($this->m_listDataPath);
            $object = unserialize($data);
            $this->m_cacheFiles   = clone $object;
        }
    }
    
    public function GetCachePath()
    {
        return $this->m_cachePath;
    }
    
    public function Clear()
    {
        CFileHelper::DeleteDir($this->GetCachePath());
        CFileHelper::CreateDir($this->GetCachePath());
    }
    
    public function AddCacheFile($fileName, CCacheCallback & $callback, $cacheFile = null, $ignore = false)
    {
        if( CFileHelper::IsAbsolutePath($fileName) )
            $key = CFileHelper::AbsoluteToRelativePath($fileName);
        
        $lastTime   = null;
        $option     = $this->m_cacheFiles->Get($key);
        if( $option ){
            return $this->_InvokeCallBack($key, $option[0], 
                $option[1], $option[2], $callback);
        }

        // valid variable
        if( !CFileHelper::IsReadable($fileName) ){
            if( $ignore ){
                return;
            }
            throw new CException('This file can not readable.<br>'.$fileName);
        }
        if( !$callback ){
            throw new CException('This callback object is invalid.');
        }

        // get absolute/relative origin file path and cache file path
        $fileName   = CFileHelper::CorrectPath($fileName);;      
        $lastTime   = CFileHelper::GetModifyTime($fileName);
            
        if( !CFileHelper::IsAbsolutePath($fileName) ){
            $absPath    = CConfig::Option(CConfig::OT_ProjPath).M_DS.$fileName;
            if( !$cacheFile ){
                $cacheFile  = $this->GetCacheFilePath($fileName);
            }
        }else{
            $absPath    = $fileName;
            if( !$cacheFile ){
                $relative   = CFileHelper::AToR($absPath, 
                    CConfig::Option(CConfig::OT_ProjPath).M_DS);
                $relative   = substr($relative, 0, strlen($relative) - 3).'dat';
                $cacheFile  = $this->GetCacheFilePath($relative);
            }
        }
        $absPath    = CFileHelper::CorrectPath($absPath);
        $this->m_cacheFiles->Add($key, array($lastTime, $absPath, $cacheFile, null));
        
        return $this->_InvokeCallBack($key, NULL, $absPath, $cacheFile, $callback);
    }
    
    protected function _InvokeCallBack($key, $lastTime, $fileName, $cacheFile, $callback)
    {
        $currTime   = CFileHelper::GetModifyTime($fileName);
        $tmpCalback = clone $callback;
        $fileExsit  = CFileHelper::IsExsit($cacheFile);
        if( $lastTime != $currTime || !$fileExsit )
        {
            if( !$fileExsit ){
                CFileHelper::CreateFile($cacheFile);
            }
            $tmpCalback->SetParam(array(true, $fileName, $cacheFile, 
                $callback->GetParam()));
            $this->m_cacheFiles->Set($key, array($currTime, $fileName,
                $cacheFile, null));
            $this->m_isNeedSave = true;
        }else{
            $tmpCalback->SetParam(array(false, $fileName, $cacheFile, 
                $callback->GetParam()));
        }
        
        $tmpCalback->Call();
    }
    
    public function GetCacheFilePath($fileName)
    {
        $tmpPath    = $this->m_cachePath.M_DS.$fileName;
        return CFileHelper::CorrectPath($tmpPath);
    }
    
    protected function _CorrectFileName($path)
    {
        if( CFileHelper::IsAbsolutePath($path) )
            $path = CFileHelper::AbsoluteToRelativePath($path);
        $path     = CFileHelper::CorrectPath($path);
        return $path;
    }
}

?>
